package org.yaac.server.file;

import static com.google.common.base.Strings.isNullOrEmpty;

import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.yaac.server.delegate.MemcacheServiceDelegate;
import org.yaac.server.util.AutoBeanUtil;
import org.yaac.shared.file.FileDownloadPath;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Text;


/**
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
// TODO : try to provide download file name based on field name?
public class FileDownloadServlet extends HttpServlet {

	private static final String PARAM_PATH = "path";
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private final Logger logger;
	
	private final MemcacheServiceDelegate memcache;
	
	@Inject
	FileDownloadServlet(Logger logger, MemcacheServiceDelegate memcache) {
		super();
		this.logger = logger;
		this.memcache = memcache;
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@SuppressWarnings("rawtypes")
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String encodedPath = req.getParameter(PARAM_PATH);

		// info is mandatory
		if (isNullOrEmpty(encodedPath)) {
			logger.severe("Can't download file with empty path");
			return;
		}
		
		FileDownloadPath path = AutoBeanUtil.decode(FileDownloadPath.class, encodedPath);
		
		switch(path.getType()) {
		case BLOBSTORE_BLOB:
			BlobstoreService blobstore = BlobstoreServiceFactory.getBlobstoreService();
			BlobKey blobKey = new BlobKey(path.getKeyStr());
			blobstore.serve(blobKey, resp);
			break;
		case DATASTORE_BLOB:
		case DATASTORE_TEXT:
			DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
			Key key = KeyFactory.stringToKey(path.getKeyStr());
			try {
				Entity entity = datastore.get(key);
				Object property = entity.getProperty(path.getFieldName());
				if (property instanceof Blob) {
					serve(((Blob) property).getBytes(), resp);
				} else if (property instanceof Text) {
					serve(((Text) property).getValue().getBytes(), resp);
				} else if (property instanceof List) {
					Object obj = ((List) property).get(path.getIndex());
					if (obj instanceof Blob) {
						serve(((Blob) obj).getBytes(), resp);	
					} else if (obj instanceof Text) {
						serve(((Text) obj).getValue().getBytes(), resp);
					}
				}
			} catch (EntityNotFoundException e) {
				e.printStackTrace();
				logger.log(Level.SEVERE, e.getMessage(), e);
			}
			
			break;
		case MEMCACHE:
			String cacheKey = path.getKeyStr();
			byte [] cache = (byte[]) memcache.get(cacheKey);
			
			if (cache == null) {
				serve("Cache expired, please upload again".getBytes(), resp);	
			} else {
				serve(cache, resp);
			}
			
			break;
		}
	}

	private void serve(byte[] bytes, HttpServletResponse resp) throws IOException {
		resp.setContentType("application/octet-stream");
		resp.setContentLength(bytes.length);
		resp.getOutputStream().write(bytes);
	}
}
